require("iuplua")
require("iupluagl")
require("luagl")
require("luaglu")
require("math")

iup.key_open()

var = {}
xrot, yrot, zrot = 0, 0, 0
xspeed, yspeed, zspeed = 0, 0, 0
z = -5
tamanho = 0.4
seta = false
z_vetor = 0


cnv = iup.glcanvas{buffer="DOUBLE", rastersize = "640x480"}
timer = iup.timer{time=10}

function timer:action_cb()
	xrot = (xrot + xspeed)%360
	yrot = (yrot + yspeed)%360
	zrot = (zrot + zspeed)%360
  iup.Update(cnv)
end

function cnv:resize_cb(width, height)
  iup.GLMakeCurrent(self)
  gl.Viewport(0, 0, width, height)

  gl.MatrixMode('PROJECTION')   -- Select The Projection Matrix
  gl.LoadIdentity()             -- Reset The Projection Matrix
  
  if height == 0 then           -- Calculate The Aspect Ratio Of The Window
    height = 1
  end

  glu.Perspective(80, width / height, 1, 5000)

  gl.MatrixMode('MODELVIEW')    -- Select The Model View Matrix
  gl.LoadIdentity()             -- Reset The Model View Matrix
end

function cnv:action(x, y)
	iup.GLMakeCurrent(self)
	gl.Clear('COLOR_BUFFER_BIT,DEPTH_BUFFER_BIT') -- Clear Screen And Depth Buffer
  
	gl.LoadIdentity()             -- Reset The Current Modelview Matrix
	gl.Translate(0,0,z)           -- Translate Into/Out Of The Screen By z

	gl.Rotate(xrot,1,0,0)
	gl.Rotate(yrot,0,1,0)
	gl.Rotate(zrot,0,0,1)

	CalculateNormal(0, 1, 0, 0, 1 + tamanho, 0)
	if var[1] and matrizDeRotacao(0, -tamanho, 0)*math.cos(yrot*math.pi/180) >= 0 then
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(0, 1, 0)        -- Set The Color To Green
		gl.Vertex( 1, 1,-1)      -- Top Right Of The Quad (Top)
		gl.Vertex(-1, 1,-1)      -- Top Left Of The Quad (Top)
		gl.Vertex(-1, 1, 1)      -- Bottom Left Of The Quad (Top)
		gl.Vertex( 1, 1, 1)      -- Bottom Right Of The Quad (Top)
		gl.End()
	end

	CalculateNormal(0, -1, 0, 0, -1 - tamanho, 0)
	if var[2] and matrizDeRotacao(0, tamanho, 0)*math.cos(yrot*math.pi/180) >= 0 then
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(1, 0.5, 0)      -- Set The Color To Orange
		gl.Vertex( 1,-1, 1)      -- Top Right Of The Quad (Bottom)
		gl.Vertex(-1,-1, 1)      -- Top Left Of The Quad (Bottom)
		gl.Vertex(-1,-1,-1)      -- Bottom Left Of The Quad (Bottom)
		gl.Vertex( 1,-1,-1)      -- Bottom Right Of The Quad (Bottom)
		gl.End()		
	end

	CalculateNormal(0, 0, 1, 0, 0, 1 + tamanho)
	if var[3] and matrizDeRotacao(0, 0, tamanho)*math.cos(zrot*math.pi/180) >= 0 then
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(1, 0, 0)        -- Set The Color To Red
		gl.Vertex( 1, 1, 1)      -- Top Right Of The Quad (Front)
		gl.Vertex(-1, 1, 1)      -- Top Left Of The Quad (Front)
		gl.Vertex(-1,-1, 1)      -- Bottom Left Of The Quad (Front)
		gl.Vertex( 1,-1, 1)      -- Bottom Right Of The Quad (Front)
		gl.End()		
	end

	CalculateNormal(0, 0, -1, 0, 0, -1 - tamanho)
	if var[4] and matrizDeRotacao(0, 0, -tamanho)*math.cos(zrot*math.pi/180) >= 0 then    
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(1, 1, 0)        -- Set The Color To Yellow
		gl.Vertex( 1,-1,-1)      -- Bottom Left Of The Quad (Back)
		gl.Vertex(-1,-1,-1)      -- Bottom Right Of The Quad (Back)
		gl.Vertex(-1, 1,-1)      -- Top Right Of The Quad (Back)
		gl.Vertex( 1, 1,-1)      -- Top Left Of The Quad (Back)
		gl.End()
	end

	CalculateNormal(-1, 0, 0, -1 - tamanho, 0, 0)
	if var[5] and matrizDeRotacao(tamanho, 0, 0)*math.cos(xrot*math.pi/180) >= 0 then
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(0, 0, 1)        -- Set The Color To Blue
		gl.Vertex(-1, 1, 1)      -- Top Right Of The Quad (Left)
		gl.Vertex(-1, 1,-1)      -- Top Left Of The Quad (Left)
		gl.Vertex(-1,-1,-1)      -- Bottom Left Of The Quad (Left)
		gl.Vertex(-1,-1, 1)      -- Bottom Right Of The Quad (Left)
		gl.End()
	end

	CalculateNormal(1, 0, 0, 1 + tamanho, 0, 0)
	if var[6] and matrizDeRotacao(-tamanho, 0, 0)*math.cos(xrot*math.pi/180) >= 0 then
		gl.Begin('QUADS')             -- Draw A Cube
		gl.Color(1, 0, 1)        -- Set The Color To Violet
		gl.Vertex( 1, 1,-1)      -- Top Right Of The Quad (Right)
		gl.Vertex( 1, 1, 1)      -- Top Left Of The Quad (Right)
		gl.Vertex( 1,-1, 1)      -- Bottom Left Of The Quad (Right)
		gl.Vertex( 1,-1,-1)      -- Bottom Right Of The Quad (Right)
		gl.End()		
	end
	
	iup.GLSwapBuffers(self)
end

function cnv:k_any(c)
	if c == iup.K_q or c == iup.K_ESC then
	return iup.CLOSE
	elseif c == iup.K_F1 then
	if fullscreen then
		fullscreen = false
		dlg.fullscreen = "No"
	else
		fullscreen = true
		dlg.fullscreen = "Yes"
		end
	elseif c == iup.K_1 then
		if var[1] then
			var[1] = false
		else
			var[1] = true
		end
	elseif c == iup.K_2 then
		if var[2] then
			var[2] = false
		else
			var[2] = true
		end
	elseif c == iup.K_3 then
		if var[3] then
			var[3] = false
		else
			var[3] = true
		end
	elseif c == iup.K_4 then
		if var[4] then
			var[4] = false
		else
			var[4] = true
		end
	elseif c == iup.K_5 then
		if var[5] then
			var[5] = false
		else
			var[5] = true
		end
	elseif c == iup.K_6 then
		if var[6] then
			var[6] = false
		else
			var[6] = true
		end
	elseif c == iup.K_PGUP then  
		z = z - 2 
	elseif c == iup.K_PGDN then  
		z = z + 2
    elseif c == iup.K_UP then  
		xspeed = xspeed - 1
		if xspeed < -1 then xspeed = -1 end
	elseif c == iup.K_DOWN then  
		xspeed = xspeed + 1
		if xspeed > 1 then xspeed = 1 end
	elseif c == iup.K_LEFT then  
		yspeed = yspeed - 1
		if yspeed < -1 then yspeed = -1 end
	elseif c == iup.K_RIGHT then  
		yspeed = yspeed + 1
		if yspeed > 1 then yspeed = 1 end
	elseif c == iup.K_s or c == iup.K_S then
		if seta then
			seta = false
		else
			seta = true
		end
	elseif c == iup.K_a then
		xrot, yrot, zrot = 0, 0, 0
		xspeed, yspeed, zspeed = 0, 0, 0
	end
end

function cnv:map_cb()
	iup.GLMakeCurrent(self)
	gl.ShadeModel('SMOOTH')            -- Enable Smooth Shading
	gl.ClearColor(0, 0, 0, 0.5)        -- Black Background
	gl.ClearDepth(1.0)                 -- Depth Buffer Setup
	gl.Enable('DEPTH_TEST')            -- Enables Depth Testing
	gl.DepthFunc('LEQUAL')             -- The Type Of Depth Testing To Do
	gl.Enable('COLOR_MATERIAL')
	gl.Hint('PERSPECTIVE_CORRECTION_HINT','NICEST')
end

function matrizDeRotacao(a, b, c)
	resultx = math.cos(yrot*math.pi/180)*math.cos(zrot*math.pi/180)*a + (math.sin(xrot*math.pi/180)*math.sin(yrot*math.pi/180)*math.cos(zrot*math.pi/180) + math.cos(xrot*math.pi/180)*math.sin(zrot*math.pi/180))*b + (-math.cos(xrot*math.pi/180)*math.sin(yrot*math.pi/180)*math.cos(zrot*math.pi/180) + math.sin(xrot*math.pi/180)*math.sin(zrot*math.pi/180))*c
	resulty = - math.cos(yrot*math.pi/180)*math.sin(zrot*math.pi/180)*a + (-math.sin(xrot*math.pi/180)*math.sin(yrot*math.pi/180)*math.sin(zrot*math.pi/180) + math.cos(xrot*math.pi/180)*math.cos(yrot*math.pi/180))*b + (math.cos(xrot*math.pi/180)*math.sin(yrot*math.pi/180)*math.sin(zrot*math.pi/180) + math.sin(xrot*math.pi/180)*math.cos(yrot*math.pi/180))*c
	resultz =  math.sin(yrot*math.pi/180)*a - math.sin(xrot*math.pi/180)*math.cos(yrot*math.pi/180)*b + math.cos(xrot*math.pi/180)*math.cos(yrot*math.pi/180)*c
	-- print(resultx .. " " .. resulty .. " " .. resultz .. " " .. xrot .. " " .. yrot  .. " " .. zrot)
	return resultz
end

function CalculateNormal(param_a, param_b, param_c, param_d, param_e, param_f)
	local k = 0.2
	local param_x, param_y, param_z = k*(param_d - param_a), k*(param_e - param_b), k*(param_f - param_c)
	local amplitude0 = math.sqrt(param_x^2 + param_y^2 + param_z^2)
	local prov1_x, prov1_y, prov1_z
	local amplitude
	local prov2_x, prov2_y, prov2_z
	
	if seta then
		gl.Begin("LINES")
		gl.Color(1, 1, 1)
		gl.Vertex(param_a, param_b, param_c)
		gl.Vertex(param_d, param_e, param_f)
		gl.End()
	
		if true then
			if param_y == 0 and param_z == 0 then
				prov1_x, prov1_y, prov1_z = param_y, -param_x, 0
			else
				prov1_x, prov1_y, prov1_z = 0, param_z, -param_y
			end
			amplitude = math.sqrt(prov1_x^2 + prov1_y^2 + prov1_z^2)
			prov1_x, prov1_y, prov1_z = prov1_x * amplitude0 / amplitude, prov1_y * amplitude0 / amplitude, prov1_z * amplitude0 / amplitude 
			prov2_x, prov2_y, prov2_z = VectorProduct(prov1_x, prov1_y, prov1_z, param_x, param_y, param_z)
			amplitude = math.sqrt(prov2_x^2 + prov2_y^2 + prov2_z^2)
			prov2_x, prov2_y, prov2_z = prov2_x * amplitude0 / amplitude, prov2_y * amplitude0 / amplitude, prov2_z * amplitude0 / amplitude
			
			gl.Begin("TRIANGLES")
			gl.Color ( 1, 1, 1)         -- White
			gl.Vertex( param_d + param_x, param_e + param_y, param_f + param_z ) -- TOP
			gl.Vertex( param_d + prov1_x, param_e + prov1_y, param_f + prov1_z ) -- VERTEX 1
			gl.Vertex( param_d + prov2_x, param_e + prov2_y, param_f + prov2_z ) -- VERTEX 2
			gl.Vertex( param_d + param_x, param_e + param_y, param_f + param_z ) -- TOP
			gl.Vertex( param_d + prov2_x, param_e + prov2_y, param_f + prov2_z ) -- VERTEX 2
			gl.Vertex( param_d - prov1_x, param_e - prov1_y, param_f - prov1_z ) -- VERTEX 3
			gl.Vertex( param_d + param_x, param_e + param_y, param_f + param_z ) -- TOP
			gl.Vertex( param_d - prov1_x, param_e - prov1_y, param_f - prov1_z ) -- VERTEX 3
			gl.Vertex( param_d - prov2_x, param_e - prov2_y, param_f - prov2_z ) -- VERTEX 4
			gl.Vertex( param_d + param_x, param_e + param_y, param_f + param_z ) -- TOP
			gl.Vertex( param_d - prov2_x, param_e - prov2_y, param_f - prov2_z ) -- VERTEX 4
			gl.Vertex( param_d + prov1_x, param_e + prov1_y, param_f + prov1_z ) -- VERTEX 1
			gl.End()			
			gl.Begin("QUADS")
			gl.Vertex( param_d + prov1_x, param_e + prov1_y, param_f + prov1_z ) -- VERTEX 1
			gl.Vertex( param_d + prov2_x, param_e + prov2_y, param_f + prov2_z ) -- VERTEX 2
			gl.Vertex( param_d - prov1_x, param_e - prov1_y, param_f - prov1_z ) -- VERTEX 3
			gl.Vertex( param_d - prov2_x, param_e - prov2_y, param_f - prov2_z ) -- VERTEX 4
			gl.End()
		end
	end
end

function VectorProduct(param_a, param_b, param_c, param_d, param_e, param_f)
	local param_i, param_j, param_k
	param_i = param_b*param_f - param_c*param_e
	param_j = param_c*param_d - param_a*param_f
	param_k = param_a*param_e - param_b*param_d
	return param_i, param_j, param_k
end

--******************************* MAIN **********************************
dlg = iup.dialog{cnv; title="Back Face Removal"}

print("\n-----------------MANUAL-----------------\n")
print("Digite os seguintes numeros para")
print("apagar/desenhar a face correspondente:\n")
print("  1 - Verde")
print("  2 - Laranja")
print("  3 - Vermelho")
print("  4 - Amarelo")
print("  5 - Azul")
print("  6 - Rosa\n")
print("Utilize as setas e as teclas \"z\" e \"x\" ")
print("para rotacionar o cubo nos tres eixos\n")
print("As teclas \"pgUp\" e \"pgDn\" aproximam e")
print("afastam o objeto\n")
print("Digite \"a\" para voltar ao estado inicial")

dlg:show()
cnv.rastersize = nil -- reset minimum limitation
timer.run = "YES"
for i = 1,6,1 do 
	var[i] = true
end

if (not iup.MainLoopLevel or iup.MainLoopLevel()==0) then
	iup.MainLoop()
end
--***********************************************************************